#include "ChatEngine.h"
#include <cstddef>
#include <cstdint>
#include "DateTime.h"
#include "EventEngine.h"
#include "Logger.h"
#include "User.h"

using namespace std::placeholders;
using namespace xncc::user;
using namespace xncc::types;
ChatEngine::ChatEngine(int id) : EventEngine(id) { user_manager_.loadUsers(); }

void ChatEngine::init()
{
    auto loginReq = std::bind(&ChatEngine::handleUserLoginReq, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<UserLoginReq>::ID, loginReq);

    auto logoutReq = std::bind(&ChatEngine::handleUserLogoutReq, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<UserLogoutReq>::ID, logoutReq);

    auto userListReq = std::bind(&ChatEngine::handleUserListReq, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<UserListReq>::ID, userListReq);

    auto chatMsg = std::bind(&ChatEngine::handleUserChatMsg, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<UserChatMsg>::ID, chatMsg);

    auto registerReq = std::bind(&ChatEngine::handleUserRegisterReq, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<UserRegisterReq>::ID, registerReq);

    auto addDelFriendReq = std::bind(&ChatEngine::handleAddDelFriendReq, this, _1, _2, _3, _4);
    registerUserFunc(MessageTraits<AddDelFriendReq>::ID, addDelFriendReq);
}

void ChatEngine::handleUserLoginReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    auto       rsp    = user_manager_.handleUserLoginReq(buf, len, tid, connId);
    const auto rspStr = rsp.toJson();
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " login req peer addr = " << getPeerAddr(connId)
                          << " connid:" << connId << " rsp:" << rspStr;
    sendData(rspStr.data(), static_cast<uint32_t>(rspStr.size()), MessageTraits<UserLoginRes>::ID, tid, connId);

    if (rsp.loginResult == "success") {
        broadcastFriendsOnOffline(rsp.id, true);
    }
}

void ChatEngine::handleUserRegisterReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    auto       rsp    = user_manager_.handleUserRegisterReq(buf, len, tid, connId);
    const auto rspStr = rsp.toJson();
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " register req peer addr = " << getPeerAddr(connId)
                          << " connid:" << connId << " rsp:" << rspStr;

    sendData(rspStr.data(), static_cast<uint32_t>(rspStr.size()), MessageTraits<UserRegisterRes>::ID, tid, connId);
}

void ChatEngine::handleAddDelFriendReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    auto       rsp    = user_manager_.handleAddDelFriendReq(buf, len, tid, connId);
    const auto rspStr = rsp.toJson();
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " add friend req peer addr = " << getPeerAddr(connId)
                          << " connid:" << connId << " rsp:" << rspStr;
    sendData(rspStr.data(), static_cast<uint32_t>(rspStr.size()), MessageTraits<AddDelFriendRes>::ID, tid, connId);
}

void ChatEngine::broadcastFriendsOnOffline(user_id_t id, bool online)
{
    const auto user = user_manager_.getUser(id);
    if (user) {
        for (auto friendId : user->friends) {
            const auto fuser = user_manager_.getUser(friendId);
            if (fuser && fuser->isOnline) {
                UserOnOffline uo  = {id, user->name, online};
                const auto    msg = uo.toJson();
                sendData(msg.data(), static_cast<uint32_t>(msg.size()), MessageTraits<UserOnOffline>::ID, fuser->tid,
                         fuser->sessionId);
                LOG_INFO(CONSOLE_LOG) << "ChatEngine " << EventEngine::id() << " broadcast user on/offline msg to "
                                      << fuser->name << " peer addr = " << getPeerAddr(fuser->sessionId)
                                      << " connid:" << fuser->sessionId << " msg:" << msg;
            }
        }
    }
}

void ChatEngine::handleUserLogoutReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId) {}
void ChatEngine::handleUserListReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    const auto rsp    = user_manager_.handleUserListReq(buf, len, tid, connId);
    const auto rspStr = rsp.toJson();
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " receive user list msg  peer addr = " << getPeerAddr(connId)
                          << " connid:" << connId << " rsp:" << rspStr;
    sendData(rspStr.data(), static_cast<uint32_t>(rspStr.size()), MessageTraits<UserListRes>::ID, tid, connId);
}
void ChatEngine::handleUserChatMsg(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    UserChatMsg msg;
    const auto  rsp = user_manager_.handleUserChatMsg(buf, len, tid, connId, msg);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " receive user chat msg  peer addr = " << getPeerAddr(connId)
                          << " connid:" << connId << " rsp:" << rsp.toJson();
    if (rsp.result == "success") {
        auto to_user = user_manager_.getUser(msg.id);
        if (to_user && to_user->isOnline) {
            const auto msgStr = msg.toJson();
            sendData(msgStr.data(), static_cast<uint32_t>(msgStr.size()), MessageTraits<UserChatMsg>::ID, to_user->tid,
                     to_user->sessionId);
            LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << "  user chat msg to " << to_user->name
                                  << " peer addr = " << getPeerAddr(to_user->sessionId)
                                  << " connid:" << to_user->sessionId << " msg:" << msgStr;
        }
    }

    const auto msgStr = rsp.toJson();
    sendData(msgStr.data(), static_cast<uint32_t>(msgStr.size()), MessageTraits<UserChatRes>::ID, tid, connId);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << "  user chat msg to " << rsp.toId
                          << " peer addr = " << getPeerAddr(connId) << " connid:" << connId << " msg:" << msgStr;
}

void ChatEngine::handleConnected(connection_id_t msg, thread_id_t tid)
{
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << " receive conn msg  peer addr = " << getPeerAddr(msg)
                          << " connid:" << msg;
}

void ChatEngine::handleDisconnected(connection_id_t msg, thread_id_t tid, const std::string& disconnectedReason)
{
    LOG_INFO(CONSOLE_LOG) << "ChatEngine " << id() << "  receive dis conn msg  peer addr = " << getPeerAddr(msg)
                          << " connid:" << msg << " reason:" << disconnectedReason;

    auto user_id = user_manager_.handleDisconnected(msg, tid);
    broadcastFriendsOnOffline(user_id, false);
}

void ChatEngine::closeClient(xncc::types::thread_id_t tid, xncc::types::connection_id_t connId) {}